using System;

namespace Microsoft.Phone.Reactive
{
    /// <summary>
    /// Observer.
    /// </summary>
    /// <typeparam name="T"></typeparam>
    public class Observer<T> : IObserver<T>
    {
        private IDisposable _unsubscriber;
        
        private readonly Action<T> _operation;
        private readonly Action<Exception> _error;
        private readonly Action _completed;

        /// <summary>
        /// Initializes new instance of <see cref="Observer{T}"/> class.
        /// </summary>
        /// <param name="operation"></param>
        /// <param name="error"></param>
        /// <param name="completed"></param>
        public Observer(Action<T> operation = null, Action<Exception> error = null, Action completed = null)
        {
            _operation = operation;
            _error = error;
            _completed = completed;
        }

        /// <summary>
        /// Subscribes to the observable provider.
        /// </summary>
        /// <param name="provider"></param>
        public virtual void Subscribe(IObservable<T> provider)
        {
            if (provider != null)
            {
                _unsubscriber = provider.SubscribeOnDispatcher().Subscribe(this);
            }
        }

        /// <summary>
        /// On-completed operation.
        /// </summary>
        public virtual void OnCompleted()
        {
            if (_completed != null)
            {
                _completed();
            }

            _unsubscriber.Dispose();
        }

        /// <summary>
        /// On-error operation.
        /// </summary>
        /// <param name="e"></param>
        public virtual void OnError(Exception e)
        {
            if (_error != null)
            {
                _error(e);
            }
        }

        /// <summary>
        /// On-next operation.
        /// </summary>
        /// <param name="value"></param>
        public virtual void OnNext(T value)
        {
            try
            {
                if (_operation != null)
                {
                    _operation(value);
                }
            }
            catch (Exception ex)
            {
                OnError(ex);
            }
            finally
            {
                OnCompleted();
            }
        }
    }
}